﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Sgr.EntityFrameworkCore;
using Sgr.Generator;
using Sgr.OmniMetrics.Domain;
using Sgr.Utilities;

namespace Sgr.OmniMetrics.Services
{
    /// <summary>
    /// 基于数据库的指标数据服务实现
    /// </summary>
    public class OmniMetricUseDatabaseService : IOmniMetricService
    {
        private readonly SgrDbContext _dbContext;
        private readonly ILogger<OmniMetricUseDatabaseService> _logger;
        private readonly INumberIdGenerator _numberIdGenerator;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbContext">数据库上下文</param>
        /// <param name="numberIdGenerator">Id生成器</param>
        /// <param name="logger">日志记录器</param>
        public OmniMetricUseDatabaseService(SgrDbContext dbContext,
            INumberIdGenerator numberIdGenerator,
            ILogger<OmniMetricUseDatabaseService> logger)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _numberIdGenerator = numberIdGenerator ?? throw new ArgumentNullException(nameof(numberIdGenerator));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        /// <summary>
        /// 删除指定时间范围内的指标数据
        /// </summary>
        /// <param name="metricKey">指标标识</param>
        /// <param name="granularity">统计粒度</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>表示异步操作的任务</returns>
        public async Task DeleteMetricsAsync(
            string metricKey,
            Granularity granularity,
            DateTime startTime,
            DateTime endTime,
            CancellationToken cancellationToken = default)
        {
            // 参数验证
            if (string.IsNullOrWhiteSpace(metricKey))
                throw new ArgumentException("指标键不能为空", nameof(metricKey));

            if (endTime < startTime)
                throw new ArgumentException("结束时间不能早于开始时间", nameof(endTime));

            cancellationToken.ThrowIfCancellationRequested();

            var set = _dbContext.Set<OmniMetricEntity>();
            try
            {
                int affected = await set
                    .Where(e => e.MetricKey == metricKey
                             && e.TimeGranularity == granularity
                             && e.TimeValueUtc >= startTime
                             && e.TimeValueUtc <= endTime)
                    .ExecuteDeleteAsync(cancellationToken);

                _logger.LogInformation("删除指标数据: 指标={MetricKey}, 粒度={Granularity}, 范围={Start}~{End}, 数量={Count}",
                    metricKey, granularity, startTime, endTime, affected);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "删除指标数据时发生错误: 指标={MetricKey}, 粒度={Granularity}, 时间范围={StartTime}~{EndTime}",
                    metricKey, granularity, startTime, endTime);
                throw;
            }
        }

        /// <summary>
        /// 获取最新的指标数据
        /// </summary>
        /// <typeparam name="T">扩展数据类型</typeparam>
        /// <param name="metricKey">指标标识</param>
        /// <param name="granularity">统计粒度</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>最新的指标数据，如果不存在则返回null</returns>
        public async Task<OmniMetric<T>?> GetLatestMetricAsync<T>(
            string metricKey,
            Granularity granularity,
            CancellationToken cancellationToken = default) where T : class
        {
            if (string.IsNullOrWhiteSpace(metricKey))
                throw new ArgumentException("指标键不能为空", nameof(metricKey));

            cancellationToken.ThrowIfCancellationRequested();

            try
            {
                // 查询最新的指标数据（按时间降序排列取第一条）
                var entity = await _dbContext.Set<OmniMetricEntity>()
                    .AsNoTracking()
                    .Where(e => e.MetricKey == metricKey && e.TimeGranularity == granularity)
                    .OrderByDescending(e => e.TimeValueUtc)
                    .FirstOrDefaultAsync(cancellationToken);

                if (entity == null)
                {
                    _logger.LogDebug("未找到最新指标数据: 指标={MetricKey}, 粒度={Granularity}", metricKey, granularity);
                    return null;
                }

                return ConvertToOmniMetric<T>(entity);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取最新指标数据时发生错误: 指标={MetricKey}, 粒度={Granularity}", metricKey, granularity);
                throw;
            }
        }

        /// <summary>
        /// 获取指定时间范围内的指标数据
        /// </summary>
        /// <typeparam name="T">扩展数据类型</typeparam>
        /// <param name="metricKey">指标标识</param>
        /// <param name="granularity">统计粒度</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>指标数据列表</returns>
        public async Task<IReadOnlyList<OmniMetric<T>>> GetMetricsAsync<T>(
            string metricKey,
            Granularity granularity,
            DateTime startTime,
            DateTime endTime,
            CancellationToken cancellationToken = default) where T : class
        {
            // 参数验证
            if (string.IsNullOrWhiteSpace(metricKey))
                throw new ArgumentException("指标键不能为空", nameof(metricKey));

            if (endTime < startTime)
                throw new ArgumentException("结束时间不能早于开始时间", nameof(endTime));

            cancellationToken.ThrowIfCancellationRequested();

            try
            {
                var entities = await _dbContext.Set<OmniMetricEntity>()
                    .AsNoTracking()
                    .Where(e => e.MetricKey == metricKey
                             && e.TimeGranularity == granularity
                             && e.TimeValueUtc >= startTime
                             && e.TimeValueUtc <= endTime)
                    .OrderBy(e => e.TimeValueUtc)
                    .ToListAsync(cancellationToken);

                _logger.LogDebug("获取指标数据: 指标={MetricKey}, 粒度={Granularity}, 范围={Start}~{End}, 数量={Count}",
                    metricKey, granularity, startTime, endTime, entities.Count);

                return entities.Select(e => ConvertToOmniMetric<T>(e)).ToList();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取指标数据失败: 指标={MetricKey}, 粒度={Granularity}, 范围={Start}~{End}",
                    metricKey, granularity, startTime, endTime);
                throw;
            }
        }

        /// <summary>
        /// 保存或更新指标数据
        /// </summary>
        /// <typeparam name="T">扩展数据类型</typeparam>
        /// <param name="metric">指标数据</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>保存后的指标数据</returns>
        public async Task<OmniMetric<T>> SaveMetricAsync<T>(
            OmniMetric<T> metric,
            CancellationToken cancellationToken = default) where T : class
        {
            // 参数验证
            Check.NotNull(metric, nameof(metric));

            if (string.IsNullOrWhiteSpace(metric.MetricKey))
                throw new ArgumentException("指标键不能为空", nameof(metric));
            cancellationToken.ThrowIfCancellationRequested();

            try
            {
                // 确保时间对齐到统计粒度
                metric.AlignTimeValue();
                metric.UpdateTime = DateTime.UtcNow;

                // 将OmniMetric转换为数据库实体
                var entity = await GetOrCreateEntityAsync(metric, cancellationToken);

                // 保存更改
                await _dbContext.SaveChangesAsync(cancellationToken);

                _logger.LogDebug(
                    "保存指标数据: 指标={MetricKey}, 粒度={Granularity}, 时间点={TimeValue}",
                    metric.MetricKey, metric.TimeGranularity, metric.TimeValue);

                return metric;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "保存指标数据时发生错误: 指标={MetricKey}, 粒度={Granularity}, 时间点={TimeValue}",
                    metric.MetricKey, metric.TimeGranularity, metric.TimeValue);
                throw;
            }
        }

        /// <summary>
        /// 批量保存或更新指标数据
        /// </summary>
        /// <typeparam name="T">扩展数据类型</typeparam>
        /// <param name="metrics">指标数据集合</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>表示异步操作的任务</returns>
        public async Task SaveMetricsAsync<T>(
            IEnumerable<OmniMetric<T>> metrics,
            CancellationToken cancellationToken = default) where T : class
        {
            // 参数验证
            Check.NotNull(metrics, nameof(metrics));

            var metricList = metrics.ToList();
            if (metricList.Count == 0)
            {
                _logger.LogDebug("批量保存指标数据: 传入集合为空，忽略操作。");
                return;
            }

            try
            {
                DateTime nowUtc = DateTime.UtcNow;
                foreach (var metric in metricList)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    if (string.IsNullOrWhiteSpace(metric.MetricKey))
                        throw new ArgumentException("指标键不能为空", nameof(metrics));

                    // 确保时间对齐到统计粒度
                    metric.AlignTimeValue();
                    metric.UpdateTime = nowUtc;

                    // 将OmniMetric转换为数据库实体
                    await GetOrCreateEntityAsync(metric, cancellationToken);
                }

                // 批量保存所有更改
                await _dbContext.SaveChangesAsync(cancellationToken);

                _logger.LogInformation("批量保存指标数据: 数量={Count}", metricList.Count);
            }
            catch (Exception ex)
            {
                //_dbContext.ChangeTracker.Clear();

                _logger.LogError(ex, "批量保存指标数据时发生错误: 数量={Count}", metricList.Count);
                throw;
            }
        }

        #region 辅助方法

        /// <summary>
        /// 获取或创建指标数据实体
        /// </summary>
        /// <typeparam name="T">扩展数据类型</typeparam>
        /// <param name="metric">指标数据</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>指标实体</returns>
        private async Task<OmniMetricEntity> GetOrCreateEntityAsync<T>(
            OmniMetric<T> metric,
            CancellationToken cancellationToken) where T : class
        {
            // 查找现有实体
            var entity = await _dbContext.Set<OmniMetricEntity>()
                .FirstOrDefaultAsync(e =>
                    e.MetricKey == metric.MetricKey &&
                    e.TimeGranularity == metric.TimeGranularity &&
                    e.TimeValueUtc == metric.TimeValue,
                    cancellationToken);

            if (entity == null)
            {
                // 创建新实体
                entity = new OmniMetricEntity
                {
                    Id = _numberIdGenerator.GenerateUniqueId(),
                    MetricKey = metric.MetricKey,
                    TimeGranularity = metric.TimeGranularity,
                    TimeValueUtc = metric.TimeValue,
                    BasicValue01 = metric.BasicValue01,
                    BasicValue02 = metric.BasicValue02,
                    BasicValue03 = metric.BasicValue03,
                    ExtendedValues = metric.ExtendedValues != null ? JsonHelper.SerializeObject(metric.ExtendedValues) : string.Empty,
                    UpdateTimeUtc = metric.UpdateTime
                };

                _dbContext.Set<OmniMetricEntity>().Add(entity);
            }
            else
            {
                // 更新现有实体
                entity.BasicValue01 = metric.BasicValue01;
                entity.BasicValue02 = metric.BasicValue02;
                entity.BasicValue03 = metric.BasicValue03;
                entity.ExtendedValues = metric.ExtendedValues != null ? JsonHelper.SerializeObject(metric.ExtendedValues) : string.Empty;
                entity.UpdateTimeUtc = metric.UpdateTime;
            }

            return entity;
        }

        /// <summary>
        /// 将数据库实体转换为OmniMetric对象
        /// </summary>
        /// <typeparam name="T">扩展数据类型</typeparam>
        /// <param name="entity">指标实体</param>
        /// <returns>指标数据对象</returns>
        private OmniMetric<T> ConvertToOmniMetric<T>(OmniMetricEntity entity) where T : class
        {
            var metric = new OmniMetric<T>
            {
                MetricKey = entity.MetricKey,
                TimeGranularity = entity.TimeGranularity,
                TimeValue = entity.TimeValueUtc,
                BasicValue01 = entity.BasicValue01,
                BasicValue02 = entity.BasicValue02,
                BasicValue03 = entity.BasicValue03,
                UpdateTime = entity.UpdateTimeUtc
            };

            // 反序列化扩展值
            if (!string.IsNullOrWhiteSpace(entity.ExtendedValues))
            {
                try
                {
                    metric.ExtendedValues = JsonHelper.DeserializeObject<T>(entity.ExtendedValues);
                }
                catch (Exception ex)
                {
                    // 记录反序列化错误，但不中断流程
                    _logger.LogWarning(ex,
                        "反序列化扩展值时发生错误: 指标={MetricKey}, ID={Id}",
                        entity.MetricKey, entity.Id);
                }
            }

            return metric;
        }

        #endregion 辅助方法
    }
}